package com.software.ioc_annotation_lib;

import android.app.Activity;
import android.util.Log;
import android.view.View;

import com.software.ioc_annotation_lib.annation.BindView;
import com.software.ioc_annotation_lib.annation.Click;
import com.software.ioc_annotation_lib.annation.ContentView;
import com.software.ioc_annotation_lib.annation_common.OnBaseCommon;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class InjectTool {
    public static void inject(Object object) {
        injectSetContentView(object);

        injectBindView(object);

        injectClick(object);

        injectEvent(object);
    }

    /*
     * 兼容版本事件 绑定
     * */
    private static void injectEvent(Object object) {
        Class<?> clazz = object.getClass();
        Method[] declaredMethods = clazz.getDeclaredMethods();

        for (Method declaredMethod : declaredMethods) {
            // 设置方法可访问
            declaredMethod.setAccessible(true);

            // 获取方法上的注解
            Annotation[] annotations = declaredMethod.getAnnotations();

            for (Annotation annotation : annotations) {
                Class<? extends Annotation> annotationType = annotation.annotationType();

                // 获取注解的OnBaseCommon注解
                OnBaseCommon onBaseCommon = annotationType.getAnnotation(OnBaseCommon.class);

                // 如果没有注解，直接跳过
                if(null == onBaseCommon){
                    continue;
                }

                //获取三要素
                String setCommonListener = onBaseCommon.setCommonListener();
                Class setCommonObjectListener = onBaseCommon.setCommonObjectListener();
                String callbackMethod = onBaseCommon.callbackMethod();

                try {
                    Activity activity = (Activity) object;

                    // 获取注解的value值，也就是控件的id
                    Method valueMethod = annotationType.getDeclaredMethod("value");
                    valueMethod.setAccessible(true);
                    // 获取注解的value值
                    String viewId = (String) valueMethod.invoke(annotation);

                    // 通过反射获取Activity的findViewById方法
                    Method findViewByIdMethod = clazz.getMethod("findViewById", int.class);
                    int id = activity.getResources().getIdentifier(viewId, "id", clazz.getPackage().getName());
                    //调用findViewById方法，获取到view对象
                    Object resultView = findViewByIdMethod.invoke(activity, id);

                    //反射
                    Method mViewMethod = resultView.getClass().getMethod(setCommonListener, setCommonObjectListener);

                    //动态代理 监听第三要素
                    Object proxy = Proxy.newProxyInstance(
                            setCommonObjectListener.getClassLoader(),// 类加载器
                            new Class[]{setCommonObjectListener},
                            (proxy1, method, args) -> declaredMethod.invoke(object, null)
                    );

                    //事件消费
                    mViewMethod.invoke(resultView, proxy);
                    Log.i("InjectTool", "Registered event: " + declaredMethod.getName() + " for view: " + viewId);
                }catch (Exception e){
                    Log.i("InjectTool", "Error registering event: " + e);
                    e.printStackTrace();
                }
            }
        }
    }

    /*
     * 把类上注解的点击事件，设置到Activity上
     * @param object MainActivity 对象 this
     * */
    private static void injectClick(Object object) {
        Class<?> clazz = object.getClass();

        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            // 设置方法可访问
            declaredMethod.setAccessible(true);

            // 获取方法上的注解
            Click click = declaredMethod.getAnnotation(Click.class);

            // 如果方法上没有注解，直接跳过
            if (null == click) {
                continue;
            }

            try {
                // 获取注解的value值，也就是控件的id
                String viewId = click.value();
                Activity activity = (Activity) object;

                // 通过反射获取Activity的findViewById方法
                Method findViewByIdMethod = clazz.getMethod("findViewById", int.class);

                // 转换注解的value值，获取到资源id
                int id = activity.getResources().getIdentifier(viewId, "id", clazz.getPackage().getName());

                //调用findViewById方法，获取到view对象
                Object resultView = findViewByIdMethod.invoke(activity, id);
                View view = (View) resultView;

                // 设置点击事件
                view.setOnClickListener(v -> {
                    try {
                        // 反射调用方法
                        declaredMethod.invoke(object);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /*
     * 把类上注解的控件，设置到Activity上
     * @param object MainActivity 对象 this
     * */
    private static void injectBindView(Object object) {
        Class<?> clazz = object.getClass();

        //和getFields的区别是，他获取的是当前类声明的所有属性，包括私有的，而getFields只获取当前类声明的公有属性
        Field[] fields = clazz.getDeclaredFields();// 获取所有属性

        for (Field field : fields) {
            // 设置私有属性可访问
            field.setAccessible(true);
            // 获取注解
            BindView bindView = field.getAnnotation(BindView.class);
            // 如果没有注解，直接跳过
            if (null == bindView) {
                continue;
            }

            try {
                String viewId = bindView.value();
                // 通过转换获取注解的完整id值
                Activity activity = (Activity) object;
                int id = activity.
                        getResources().// 获取资源对象
                                getIdentifier(
                                viewId,
                                "id",
                                clazz.getPackage().getName()// 包名
                        );// 获取资源id

                // 通过反射获取Activity的findViewById方法
                Method findViewByIdMethod = clazz.getMethod("findViewById", int.class);

                // 调用findViewById方法，获取到view对象
                Object view = findViewByIdMethod.invoke(activity, id);

                // 把view对象设置到field对象中
                field.setAccessible(true);
                field.set(object, view);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /*
     * 把类上的注解，设置到activity上
     * @param object MainActivity 对象 this
     * */
    private static void injectSetContentView(Object object) {
        Class<?> clazz = object.getClass();

        // 获取类上的注解
        ContentView mContentView = clazz.getAnnotation(ContentView.class);

        // 如果类上没有注解，直接返回
        if (null == mContentView) {
            Log.d("InjectTool", "类上没有注解");
        } else {
            Log.d("InjectTool", "类上有注解");
            String layoutName = mContentView.value();
            Activity activity = (Activity) object;
            int layoutId = activity.getResources().getIdentifier(layoutName, "layout", activity.getPackageName());

            try {
                Method setContentViewMethod = clazz.getMethod("setContentView", int.class);

                // 调用setContentView方法
                setContentViewMethod.invoke(object, layoutId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
